home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / metamail / contrib / ServiceMail / src / dspd / dspd.c next >
Encoding:
C/C++ Source or Header  |  1993-05-10  |  13.8 KB  |  659 lines

  1. /*Copyright (c)  1993 Enterprise Integration Technologies Corporation
  2.  
  3. Permission to use, copy, modify, distribute, and sell this software and
  4. its documentation for any purpose is hereby granted without fee, provided
  5. that (i) the above copyright notices and this permission notice appear in
  6. all copies of the software and related documentation, and (ii) the name of
  7. Enterprise Integration Technologies Corporation may not be used in any
  8. advertising or publicity relating to the software without the specific,
  9. prior written permission of Enterprise Integration Technologies Corporation.
  10.  
  11. THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
  12. EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
  13. WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
  14.  
  15. IN NO EVENT SHALL ENTERPRISE INTEGRATION TECHNOLOGIES CORPORATION  BE
  16. LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF
  17. ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  18. PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY
  19. THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  20. PERFORMANCE OF THIS SOFTWARE.
  21. */
  22. #include "dspd.h"
  23.  
  24. int main(argc,argv)
  25.   int argc;
  26.   char **argv;
  27.   {
  28.     pid_t pid;
  29.     strcpy(executable,MESH_PATH);
  30.     gid=getegid();
  31.     uid=geteuid();    
  32.     init();
  33.     procopts(argc,argv);
  34. #ifdef USESYSLOG
  35.     openlog("dsp",(LOG_PID | LOG_CONS),facility);
  36. #endif    
  37.     if(chdir(spooldir))
  38.     {
  39.       fprintf(stderr,"dspd: Invalid spool directory.\n");
  40.       exit(1);
  41.     }
  42.     /*Now become a daemon*/
  43.     if((rund)&&daemonize())
  44.     {
  45.       fprintf(stderr,"Unable to become a daemon. Aborting.\n");
  46.       exit(2);
  47.     }
  48.     writepid();
  49.     signal(SIGUSR1,sighalt);
  50.     mainloop();
  51. #ifdef USESYSLOG
  52.     syslog(facility | LOG_ALERT,"My work here is done.");
  53. #endif    
  54.     delpid();
  55.     exit(0);    
  56.   }        
  57.  
  58. int daemonize() /*This function modeled closely after Stevens*/
  59.   {
  60.     pid_t pid;
  61.  
  62.     if((pid=fork())<0)
  63.       return(-1);
  64.     if(pid!=0)
  65.       exit(0);
  66.  
  67.     setsid();
  68.     umask(0);
  69.     return(0);
  70.   }
  71.  
  72. int procopts(argc,argv)
  73.   int argc;
  74.   char **argv;
  75.   {
  76.     int ret;
  77.     while((ret=getopt(argc,argv,"q:s:k:d:m:bQ:t:p:e:l:n"))!=-1)
  78.     {
  79.       switch(ret)
  80.       {
  81.         case 's':
  82.           if((ret=strtosecs(optarg))!=-1)
  83.             sleepwhenempty=ret;
  84.           break;
  85.         case 'q':
  86.           if((ret=strtosecs(optarg))!=-1)
  87.             queuetime=ret;
  88.           break;
  89.         case 'k':
  90.           if((ret=strtosecs(optarg))!=-1)
  91.             killtime=ret;
  92.           break; 
  93.         case 'd':
  94.           if(strlen(optarg)>MAXPATHLEN)
  95.           {
  96.             fprintf(stderr,"Invalid spool path--too long\n");
  97.             exit(1);
  98.           }
  99.           else
  100.             strcpy(spooldir,optarg);
  101.           break;
  102.         case 'e':
  103.           if(strlen(optarg)>MAXPATHLEN)
  104.           {
  105.             fprintf(stderr,"Invalid program--path too long\n");
  106.             exit(1);
  107.           }
  108.           else
  109.             strcpy(executable,optarg);
  110.           break;
  111.         case 'b':
  112.           rund=1;
  113.           break;
  114.         case 'm':
  115.           if((ret=strtosecs(optarg))!=-1)
  116.             maxmeshes=(ret>MAX_MESHES)?MAX_MESHES:ret;
  117.           break;
  118.         case 'Q':
  119.           if((ret=strtosecs(optarg))!=-1)
  120.             queuesize=(ret>QUEUESIZE)?QUEUESIZE:ret;
  121.           break;
  122.         case 't':
  123.           if((ret=strtosecs(optarg))!=-1)
  124.             timeval=ret;
  125.           break;
  126.         case 'p':
  127.           if((ret=strtosecs(optarg))!=-1)
  128.             prival=ret;
  129.           break;  
  130.         case 'l':
  131.           switch(atoi(optarg))
  132.           {
  133.             case '0': facility=LOG_LOCAL0;
  134.                       break;
  135.             case '1': facility=LOG_LOCAL1;
  136.                       break;
  137.             case '2': facility=LOG_LOCAL2;
  138.                       break;
  139.             case '3': facility=LOG_LOCAL3;
  140.                       break;
  141.             case '4': facility=LOG_LOCAL4;
  142.                       break;
  143.             case '5': facility=LOG_LOCAL5;
  144.                       break;
  145.             case '6': facility=LOG_LOCAL6;
  146.                       break;
  147.             case '7': facility=LOG_LOCAL7;
  148.                       break;
  149.           }              
  150.           logfac=atoi(optarg);
  151.           break; 
  152.         case 'n': nounlink=1;
  153.                   break;
  154.         case '?':
  155.            fprintf(stderr,"dspd: dspd [-qtime] [-stime]\n");
  156.            exit(1);
  157.         default:
  158.           break;
  159.       }
  160.     }
  161.   }
  162.       
  163. strtosecs(arg)
  164.   char *arg;
  165.   {
  166.     int units,last,time;
  167.     char *foo;
  168.  
  169.     switch(arg[last=(strlen(arg)-1)]) {
  170.       case 'h':
  171.         units=3600;
  172.         arg[last]='\0';
  173.         break;
  174.       case 'm':
  175.         units=60;
  176.         arg[last]='\0';
  177.         break;
  178.       case 's':
  179.         units=1;
  180.         arg[last]='\0';
  181.         break;
  182.       case '0':
  183.       case '1':
  184.       case '2':
  185.       case '3':
  186.       case '4':
  187.       case '5':
  188.       case '6':
  189.       case '7':
  190.       case '8':
  191.       case '9':
  192.         units=1;
  193.         break;
  194.       default:
  195.         return(-1);
  196.         break;
  197.     }
  198.  
  199.     if((!(time=strtol(arg,&foo,10)))&&(foo==arg))
  200.       return(-1);
  201.     time*=units;
  202.     return(time);
  203.   }
  204.  
  205. mainloop()
  206.   {
  207.     int test;
  208.     while(1)
  209.     {
  210.       if((totalmsgs=queuemsgs())>0)
  211.         procmsgs();
  212.       else
  213.       {
  214.         meshreap();
  215.         if(!meshes)
  216.         {
  217.           if(sleepwhenempty)
  218.             sleep(sleepwhenempty);
  219.           else
  220.             return;
  221.         }
  222.       }
  223.       if(halt && (!meshes))
  224.         return;
  225.     }
  226.   }
  227.  
  228. queuemsgs()
  229.   {
  230.     DIR *dirp;
  231.     static struct dirdat msglist[QUEUESIZE];
  232.     struct dirent *p;
  233.     static struct dirdat p2;
  234.     struct dirdat *new,*potential=&p2;
  235.     int sz,size=0;
  236.     register int j,k,l;
  237.  
  238.     if(!(dirp=opendir(spooldir)))
  239.       error(E_OPENDIR,spooldir);
  240.  
  241.     /*We use a fixed size priority queue to get the best n elements...*/
  242.     while(p=readdir(dirp))
  243.     {
  244.       strcpy(potential->d_name,p->d_name);
  245.       if((!readok(potential))||beingprocessed(potential))
  246.         continue;
  247.       if(size<(queuesize-1))
  248.       {
  249.         memcpy((char *)&msglist[++size],(char *)potential,sizeof(struct dirdat));
  250.         msgplist[size]=msglist+size;
  251.         j=size;
  252.         while((j!=1)&&(nicecmp(msgplist[j/2],potential)<=0))
  253.         {
  254.           msgplist[j]=msgplist[j/2];
  255.           j/=2;
  256.         }
  257.         msgplist[j]=msglist+size;
  258.       }
  259.       else
  260.       {
  261.         if(nicecmp(msgplist[1],potential)<0)
  262.           continue;
  263.         /*Swap contents w/ nicest value--then reheap*/
  264.         memcpy((char *)msgplist[1],(char *)potential,sizeof(struct dirdat));
  265.         new=msgplist[1];
  266.         DOWNHEAP;
  267.       }
  268.     }
  269.  
  270.     sz=size;
  271.     for(l=size;l>0;)
  272.     {
  273.       new=msgplist[1];
  274.       msgplist[1]=msgplist[l];
  275.       msgplist[l--]=new;
  276.       size--;
  277.       new=msgplist[1];
  278.       DOWNHEAP;
  279.     }
  280.     closedir(dirp);
  281.     nextscanat=time((time_t *)NULL)+queuetime;
  282.     return(sz);
  283.   }
  284.   
  285. nicecmp(d1,d2)
  286.   struct dirdat *d1,*d2;
  287.   {
  288.     int p1,p2;
  289.     p1=(*(d1->d_name))*prival+(d1->buf.st_mtime)*timeval;
  290.     p2=(*(d2->d_name))*prival+(d2->buf.st_mtime)*timeval;
  291.     if(p1==p2)
  292.       return(0);
  293.     if(p1<p2)
  294.       return(1);
  295.     return(-1);
  296.   }
  297.  
  298. procmsgs()
  299.   {
  300.     int mesh,lastmesh;
  301.     time_t killtime;
  302.     int msgs=1;
  303.  
  304.     while((msgs<=totalmsgs)&&(time((time_t *)NULL)<nextscanat))
  305.     {
  306.       for(mesh=0;(mesh<maxmeshes)&&(msgs<=totalmsgs);mesh++)
  307.       {
  308.         time_t age,tolive;
  309.         if(!procdata[mesh].pid)
  310.           if((!halt)&&meshstart(mesh,msgs))
  311.             msgs++;
  312.           else
  313.             continue;
  314.             
  315.         age=time((time_t *)NULL)-procdata[mesh].starttime;
  316.         if(age>killtime)
  317.           meshkill(mesh);
  318.       }
  319.       meshreap();
  320.       if((!meshes)&&(halt))
  321.         return;
  322.       if((meshes==maxmeshes)||halt)
  323.       {
  324.         timewait();
  325.         meshreap();
  326.       }
  327.     }          
  328.   }
  329.  
  330. meshreap()
  331.   {
  332.     int status;
  333.     struct rusage usage;
  334.     pid_t pid;
  335.     while(pid=wait3(&status,WNOHANG,&usage))
  336.     {
  337.       if(pid>0)
  338.         meshclean(pid,status,usage);
  339.       else
  340.       {
  341.         if(errno==EINTR)
  342.            continue;
  343.         if(errno==ECHILD)
  344.            return(0);
  345.         error(E_WAIT,NULL);
  346.       }
  347.     }
  348.   }
  349.  
  350. timewait()
  351.   {
  352.     int mesh;
  353.     time_t earliest,atime,now,when;
  354.     pid_t pid;
  355.     int first;
  356.     int status;
  357.     struct rusage usage;
  358.  
  359.     earliest=time((time_t *)NULL);
  360.     for(mesh=0;mesh<maxmeshes;mesh++)
  361.     {
  362.       if(!(procdata[mesh].pid))
  363.         continue;
  364.  
  365.       if(earliest>(procdata[mesh].starttime))
  366.       {
  367.         first=mesh;
  368.         earliest=procdata[mesh].starttime;
  369.       }
  370.     }        
  371.  
  372.     signal(SIGALRM,sigbogo);
  373. #ifdef USESIGINTERRUPT
  374.     siginterrupt(SIGALRM,1);
  375. #endif
  376.  
  377.     now=time((time_t *)NULL);
  378.     if((now-earliest)>killtime)
  379.       atime=0;
  380.     else
  381.       atime=killtime-(now-earliest);
  382.  
  383.     when=time((time_t *)NULL)+atime;
  384.     alarm(atime);
  385.  
  386.     if((pid=wait3(&status,0,&usage))<0)
  387.     {
  388.       now=time((time_t *)NULL);
  389.       if((errno==EINTR)&&(now>=when))
  390.       {
  391.         meshkill(first);
  392.         return;
  393.       }
  394.       else
  395.       {
  396.         error(E_WAIT,NULL);
  397.         if(errno==ECHILD)
  398.           for(mesh=0;mesh<maxmeshes;mesh++)
  399.             procdata[mesh].pid=0;
  400.         return;
  401.       }
  402.     } 
  403.     meshclean(pid,status,&usage);
  404.   }
  405.     
  406. meshstart(mesh,msg)
  407.   int mesh;
  408.   int msg;
  409.   {
  410.     pid_t pid;
  411.     char out[1024];
  412.     
  413.     strcpy(procdata[mesh].d_name,msgplist[msg]->d_name);
  414. #ifndef USEVFORK
  415.     if((pid=fork())<0)
  416. #else
  417.     if((pid=vfork())<0)
  418. #endif
  419.     {
  420.       error(E_NOFORK,NULL);
  421.       return(0);
  422.     }
  423.  
  424.     if(pid==0) /*Child*/
  425.     {
  426.       char logflag[4];
  427.       sprintf(out,"Started processing of %s",msgplist[msg]->d_name);
  428. #ifdef USESYSLOG
  429.       syslog(NORM_LEVEL | facility,out);
  430. #endif
  431.       sprintf(logflag,"-l%d",logfac);
  432.       execlp(executable,executable,logflag,msgplist[msg]->d_name,(char *)0);
  433.       error(E_NOEXEC,executable);
  434.       _exit(1);
  435.     }
  436.     else /*parent*/
  437.     {
  438.       procdata[mesh].pid=pid;
  439.       procdata[mesh].starttime=time((time_t *)NULL);
  440.       meshes++;
  441.     }
  442.     return(1);
  443.   } 
  444.  
  445. meshkill(mesh)
  446.   int mesh;
  447.   {
  448.     char out[1024];
  449.  
  450.     sprintf(out,"Processing of %s timed out. ",procdata[mesh].d_name);
  451.     if(kill(procdata[mesh].pid,SIGTERM))
  452.     {
  453.       error(E_NOKILL,procdata[mesh].d_name);
  454.       sprintf(out+strlen(out),"\nTried to kill but failed. ");
  455.     }
  456.     else
  457.       sprintf(out+strlen(out,"\nTerminated w/ extreme prejudice. "));
  458.       
  459.     if(!nounlink)
  460.     {
  461.       if(unlink(procdata[mesh].d_name)&&(errno!=ENOENT)) 
  462.       {
  463.         error(E_UNLINK,procdata[mesh].d_name);
  464.         sprintf(out+strlen(out),"File removal failed.");
  465.       }
  466.       else
  467.         sprintf(out+strlen(out),"File removed.");
  468.     }
  469.     
  470.     syslog(NORM_LEVEL,out);
  471.     meshreap();
  472.   }
  473.  
  474. meshclean(pid,status,rusage)
  475.   pid_t pid;
  476.   int status;
  477.   struct rusage *rusage;
  478.   {
  479.     int mesh;
  480.     char out[1024];
  481.  
  482.     for(mesh=0;mesh<maxmeshes;mesh++)
  483.       if(procdata[mesh].pid==pid) break;
  484.  
  485.     meshes--;
  486.     if(halt)
  487.       logstat();
  488.     procdata[mesh].pid=0;
  489. #ifdef USESYSLOG
  490.     if(status)
  491.       sprintf(out,"Processing seems to have failed on [%d] %s. Exit status %d",
  492.         pid,procdata[mesh].d_name,status);
  493.     else
  494.       sprintf(out,"Processing seems to have terminated normally on [%d] %s",
  495.         pid,procdata[mesh].d_name);
  496.     syslog(NORM_LEVEL,out);
  497.     sprintf(out,"%s used User time: %ld.%ld, System time: %ld.%ld ",
  498.       procdata[mesh].d_name,
  499.       rusage->ru_utime.tv_sec,rusage->ru_utime.tv_usec,
  500.       rusage->ru_stime.tv_sec,rusage->ru_stime.tv_usec);
  501.     syslog(NORM_LEVEL,out);
  502. #endif    
  503.     if(!nounlink)
  504.       if(unlink(procdata[mesh].d_name))
  505.       {
  506.         if(errno!=ENOENT)
  507.           error(E_EXISTS,procdata[mesh].d_name);
  508.       }
  509.   }    
  510.  
  511. beingprocessed(d)
  512.   struct dirdat *d;
  513.   {
  514.     int mesh;
  515.     for(mesh=0;mesh<maxmeshes;mesh++)
  516.     {
  517.       if(procdata[mesh].pid)
  518.       {
  519.         if(!strcmp(procdata[mesh].d_name,d->d_name))
  520.           return(1);
  521.       }
  522.     }
  523.     return(0);
  524.   }
  525.   
  526. readok(d)
  527.   struct dirdat *d;
  528.   {
  529.     struct stat buf;
  530.  
  531.     if(*(d->d_name)=='.')
  532.       return(0);
  533.     if(!strcmp(d->d_name,"core"))
  534.       return(0);
  535.     if(stat(d->d_name,&buf))
  536.     {
  537.       error(E_STAT,d->d_name);
  538.       return(0);
  539.     }
  540.     memcpy((char *)&(d->buf),(char *)&buf,sizeof(buf));
  541.     if(!(S_ISREG(buf.st_mode)))
  542.       return(0);
  543.     
  544.     if(uid==buf.st_uid)
  545.     {
  546.       if(buf.st_mode&S_IRUSR)
  547.         return(1);
  548.       else
  549.         return(0);
  550.     }
  551.     if(gid==buf.st_gid)
  552.     {
  553.       if(buf.st_mode&S_IRGRP)
  554.         return(1);
  555.       else
  556.         return(0);
  557.     }
  558.     if(buf.st_mode&S_IROTH)
  559.         return(1);
  560.       else
  561.         return(0);
  562.   }
  563.  
  564. error(e,c)
  565.   int e;
  566.   char *c;
  567.   {
  568.     char line[1024];
  569.  
  570.     strcpy(line,err[e].msg);
  571.      if(c)
  572.       strcat(line,c);
  573.     if(err[e].status & SYSTEM)
  574.       strcat(line," because %m");
  575. #ifdef USESYSLOG
  576.     syslog(facility | err[e].level,line);
  577. #endif    
  578.     if(err[e].status & FATAL)
  579.     {
  580.       delpid();
  581.       exit(1);
  582.     }
  583.   }      
  584.       
  585. sigbogo(signo)
  586.   int signo;
  587.   {
  588.  
  589.     /*bogo func...*/
  590.   }
  591. sighalt(signo)
  592.   int signo;
  593.   {
  594.     char out[1024];
  595.     sleepwhenempty=0;
  596.     halt=1;
  597.     sprintf(out,"Halt signal caught. Preparing to shutdown.");
  598. #ifdef USESYSLOG
  599.     syslog(LOG_ALERT | facility,out);
  600. #endif    
  601.     logstat();
  602.   }
  603.  
  604. logstat()
  605.   {
  606.     char out[1024];
  607.     sprintf(out,"%d subprocesses still running. ");
  608.     if(meshes)
  609.       strcat(out,"I think I can. I think I can.",meshes);
  610.     else
  611.       strcat(out,"I thought I could. I thought I could.");
  612. #ifdef USESYSLOG
  613.     syslog(LOG_ALERT | facility,out);
  614. #endif
  615.   }
  616.         
  617. init()
  618. {
  619.   int mesh;
  620.   for(mesh=0;mesh<maxmeshes;mesh++)
  621.     procdata[mesh].pid=0;
  622. }  
  623.     
  624. writepid()
  625.   {
  626.     FILE *fp;
  627.     pid_t pid;
  628.     char *h;
  629.     char buf[MAXPATHLEN+1];
  630.     char *getenv();
  631.     
  632.     pid=getpid();
  633.  
  634.     if(fp=fopen(PIDFILE,"w"))
  635.       fprintf(fp,"%d\n",pid);
  636.     else
  637.       error(E_NOPID,NULL);
  638.     fclose(fp);
  639.   }
  640.  
  641. delpid()
  642.   {
  643.     char buf[MAXPATHLEN+1];
  644.     char *getenv();
  645.     char *h;    
  646.  
  647.      if(unlink(PIDFILE)<0)
  648.       error(E_STILLPID,NULL);
  649.   }
  650.       
  651.       
  652.  
  653.         
  654.         
  655.  
  656.     
  657.     
  658.     
  659.